package com.shuffleview

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.graphics.Rect
import android.os.Handler
import android.os.Message
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import java.lang.ref.WeakReference
import java.util.*
import kotlin.collections.ArrayList

/**
 * 采用的是随机数的方式随机显示view;但是如果只是想达到view位置的错落，并非是要view的位置随机，完全可以预置一些
 * 位置，然后每次随机取几个位置，一样可以达到view错落的效果，并且效率更高，避免检测是否重叠;本次实现使用的还是随机生成检测
 * 直到找到一定数量的彼此互不碰撞的位置点生成气泡
 */
const val AMPLITUDE = 10
const val EDGE = 60
val removalTag = Object()
private val TAG = "ShuffleLayout"

private val itemSpace = 80

class ShuffleLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
    : ViewGroup(context, attrs, defStyleAttr) {

    /**
     * 最多允许显示的view数量
     */
    private var maxCount = 0

    private val random = Random()
    /**
     * 保存已经添加view的信息，方便新生成的view的碰撞检测
     */
    private val views = ArrayList<ItemView>()


    lateinit var datas: List<Float>

    private var rest = ArrayList<Float>()

    init {
        val typeArray = context.obtainStyledAttributes(attrs, R.styleable.ShuffleLayout)
        maxCount = typeArray.getInt(R.styleable.ShuffleLayout_max_count, 6)
        typeArray.recycle()
    }

    /**
     * 延迟加载Handler
     */
    private val mHandler: H by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { H(this) }

    /**
     * Handler更新每一帧的数据。必须使用Handler，大批量的动画想都不用想是低效率的
     */
    private class H(shuffleLayout: ShuffleLayout) : Handler() {
        private val weakRef = WeakReference<ShuffleLayout>(shuffleLayout)
        private val INTERVAL: Long = 62// 1000 ÷ 16 = 62
        override fun handleMessage(msg: Message?) {
            update()
            sendEmptyMessageDelayed(0, INTERVAL)
        }

        private fun update() {
            val parent = weakRef.get()
            val count = parent!!.childCount
            for (k in 0 until count) {
                val childView = parent.getChildAt(k)
                if (childView.tag == removalTag)//更新的过程中避免对要移除的view更新
                    continue
                val speed = childView.getTag(R.string.KEY_SPEED) as Float
                val y = childView.getTag(R.string.KEY_YPOSITION) as Float
                val isUp = childView.getTag(R.string.KEY_DIRECTION) as Int == 0
                if (isUp) {
                    childView.y -= speed
                } else childView.y += speed
                val amplitude = childView.y - childView.top
                if (Math.abs(amplitude) > AMPLITUDE)
                    reverseDirection(childView, isUp)
            }
        }

        private fun reverseDirection(childView: View, up: Boolean) {
            if (up) childView.setTag(R.string.KEY_DIRECTION, 1)
            else childView.setTag(R.string.KEY_DIRECTION, 0)
        }
    }

    private inner class ViewClick : OnClickListener {
        override fun onClick(v: View) {
            //用id之类的标记 提交到后台 更新收集的黑钻数量
            //reportToServerCollected(datas.get(v.getTag(R.string.KEY_INDEX) as Int).id)
            expel(v)
        }

    }

    /**
     * animate view out and remove it
     */
    private fun expel(v: View) {
        v.tag = removalTag
        //由于我们是通过setX setY方式设置的坐标，onLayout都是layout(0,0,x,x)所以不能依照top因为top是0
        val duration = Math.max((v.top / measuredHeight.toFloat() * 550).toLong(), 200)
        v.animate()
                .translationY((-(v.top + v.height)).toFloat())
                .alpha(0f)
                .setDuration(duration)
                .setInterpolator(AccelerateInterpolator())
                .setListener(object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator?) {
                        removeView(v)
                        views.remove(v)
                        compensate()
                    }
                })
                .start()

    }

    /**
     * 当移除view后，此方法用于补充view
     */
    private fun compensate() {
        if (rest.isEmpty())
            return
        val profit = rest.removeAt(0)
        val itemView = ItemView(context)
        addView(itemView)
        bindViewData(itemView, datas.indexOf(profit), profit)
        measureChild(itemView, 0, 0)
        layoutAndShowView(itemView)
        showView(itemView)
    }

    private fun showView(itemView: ItemView) {
        itemView.alpha = 0f
        itemView.animate()
                .alpha(1f)
                .setDuration(350)
                .start()
    }

    fun setData(items: List<Float>) {
        this.datas = items
        this.rest.addAll(items.subList(6, datas.size))
        post {
            views.clear()
            proceed()
            triggerLoop()
        }

    }


    private fun proceed() {
        datas.forEachIndexed({ index, profit ->
            if (index >= maxCount)
                return
            val itemView = ItemView(context)
            addView(itemView)
            //bindView必须在measure之前，否则文字不显示
            bindViewData(itemView, index, profit)
            measureChild(itemView, 0, 0)
            layoutAndShowView(itemView)
        })
    }

    private fun bindViewData(itemView: ItemView, index: Int, profit: Float) {
        itemView.setDiamondProfit(profit)
        itemView.setTag(R.string.KEY_INDEX, index)//保存下标,为了方便确定点击了哪个
        itemView.setTag(R.string.KEY_SPEED, (Random().nextFloat() + 0.2f))//设置一个初始的随机速度
        itemView.setTag(R.string.KEY_DIRECTION, Random().nextInt(2))//设置一个随机运动方向
        itemView.setTag(R.string.KEY_YPOSITION, itemView.y)//保存初始位置的坐标
        itemView.setOnClickListener(ViewClick())
    }

    private fun layoutAndShowView(itemView: ItemView) {
        val itemWidth = itemView.measuredWidth
        val itemHeight = itemView.measuredHeight
        while (true) {
            val left = random.nextInt(measuredWidth - itemWidth)
            val top = random.nextInt(measuredHeight - itemHeight)
            if (intersectEdge(left, top, left + itemWidth, top + itemHeight)) continue
            val right = left + itemWidth
            val bottom = top + itemHeight
            var success = true
            for (item in views) {
                if (hasCollided(left - itemSpace, top - itemSpace, right + itemSpace, bottom + itemSpace, item)) {
                    success = false
                    break
                }
            }

            if (success) {
                itemView.layout(left, top, left + itemWidth, top + itemHeight)
                views.add(itemView)
                break
            }
        }

    }

    private fun intersectEdge(left: Int, top: Int, right: Int, bottom: Int): Boolean {
        return left < EDGE || top < EDGE || right > width - EDGE || bottom > height - EDGE
    }

    /**
     * 碰撞检测
     */
    private fun hasCollided(left: Int, top: Int, right: Int, bottom: Int, item: ItemView): Boolean {
        val testRectF = Rect(left, top, right, bottom)
        return testRectF.intersect(Rect(item.left, item.top, item.right, item.bottom))

    }

    private fun triggerLoop() {
        mHandler.sendEmptyMessage(0)
    }


    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        /*no op*/
    }


}


